`timescale 1ns / 1ps
module dpi_interface # (
    parameter DATA_WIDTH = 32,
    parameter ADDR_WIDTH = 32
) (
    input logic clk,
    input logic rst,

    output logic write_en,
    output logic read_en,
    output logic req_valid,

    input logic busy,
    output logic ready,

    output logic[ADDR_WIDTH-1:0] req_addr, // from SB to axi_ram
    output logic[DATA_WIDTH-1:0] req_data, // from SB to axi_ram

    input logic resp_valid,

    input logic[ADDR_WIDTH-1:0] resp_addr, // from axi_ram to SB
    input logic[DATA_WIDTH-1:0] resp_data // from axi_ram to SB

);
    import "DPI-C" function void hello();
    import "DPI-C" function int receive_requset_from_sb(output int rev_data, output int rev_addr, output int operate);
    import "DPI-C" function int send_response_to_sb(input int send_data, input int send_addr, input int resp_valid); 

    // local var
    logic [3:0] counter;

    logic [31:0] read_write; //0:read 1:write
    logic [DATA_WIDTH-1:0] req_data_reg;
    logic [ADDR_WIDTH-1:0] req_addr_reg;
    logic [DATA_WIDTH-1:0] resp_data_reg;
    logic [ADDR_WIDTH-1:0] resp_addr_reg;

    // 

    // local signal
    logic write_en_reg;
    logic read_en_reg;
    logic req_valid_reg;

    logic ready_reg;
    logic resp_valid_reg;

    logic send_to_sb_valid;

    logic success;

    // dpi var, connect to dpi
    int rev_data;
    int rev_addr;
    int operate;

    int send_data;
    int send_addr;
    int send_resp_valid;

    int valid;


    always_comb begin
        if (rst) begin
            success = 1'b0;
        end else begin
            success = resp_valid_reg & send_to_sb_valid;
        end
    end

    typedef enum logic[1:0] {
        IDLE = 2'b00, //listen req from sb
        WRITE = 2'b01, //wait for axi
        WAIT = 2'b10, //wait for axi
        READ = 2'b11 //wait for sb
    } state_t;

    state_t state, next_state;

    always @(posedge clk or negedge rst) begin
        if (rst == 1'b1) begin
            state <= IDLE;
        end else begin
            state <= next_state;
        end
    end

    always_comb begin
        case (state)
            IDLE: begin
                if(req_valid_reg && ~busy) begin
                    if (write_en) begin
                        next_state = WRITE;
                    end else if (read_en) begin
                        next_state = READ;
                    end else begin
                        next_state = IDLE;
                    end
                end else begin
                    next_state = IDLE;
                end
            end
            WRITE: begin
                if (busy && resp_valid) begin
                    next_state = WAIT;
                end else begin
                    next_state = WRITE;
                end
            end
            READ: begin
                if (busy && resp_valid) begin
                    next_state = WAIT;
                end else begin
                    next_state = READ;
                end
            end
            WAIT: begin
                if (success) begin
                    next_state = IDLE;
                end else begin
                    next_state = WAIT;
                end
            end
        endcase
    end


    // connect to port
    assign req_valid = req_valid_reg;
    assign write_en = write_en_reg;
    assign read_en = read_en_reg;
        
    assign req_addr = req_addr_reg;
    assign req_data = req_data_reg;

    assign ready = ready_reg;

    always_ff @(posedge clk or negedge rst) begin : resp_from_axi
        if (rst) begin
            resp_addr_reg <= 0;
            resp_data_reg <= 0;
            resp_valid_reg <= 0;
        end else begin
            resp_addr_reg <= resp_addr;
            resp_data_reg <= resp_data;
            resp_valid_reg <= resp_valid;
        end
    end


    always_ff @(posedge clk or negedge rst) begin : listen_and_send_with_sb
        if (rst) begin
            req_valid_reg <= 0;
            write_en_reg <= 0;
            read_en_reg <= 0;
            ready_reg <= 0;
            send_to_sb_valid <= 0;
            req_addr_reg <= '0;
            req_data_reg <= '0;

        end else begin
            case (state)
                IDLE: begin
                    if (!req_valid_reg) begin
                        req_valid_reg <= 0;
                        write_en_reg <= 0;
                        read_en_reg <= 0;
                        send_to_sb_valid <= 0;

                        req_addr_reg <= '0;
                        req_data_reg <= '0;

                        ready_reg <= 0;
                        
                        // listen for sb
                        valid = receive_requset_from_sb(rev_data, rev_addr, operate);
                        if (valid) begin
                            if (operate == 0) begin
                                write_en_reg <= 0;
                                read_en_reg <= 1;
                            end else begin
                                write_en_reg <= 1;
                                read_en_reg <= 0;
                            end
                            req_valid_reg <= 1;
                            req_addr_reg <= rev_addr;
                            req_data_reg <= rev_data;
                        end
                    end
                end
                WRITE: begin
                    // do nothing and wait for axil service writes
                    // req_valid_reg <= 0;
                    // ready_reg <= 0;
                end
                READ: begin
                    // do nothing and wait for axil service reads
                    // req_valid_reg <= 0;
                    // ready_reg <= 0;
                end
                WAIT: begin
                    req_valid_reg <= 0;
                    ready_reg <= 1;
                    // send to sb
                    if (resp_valid_reg && ~send_to_sb_valid) begin
                        

                        send_data = resp_data_reg;
                        send_addr = resp_addr_reg;
                        send_resp_valid = resp_valid_reg;
                        valid = send_response_to_sb(send_data, send_addr, send_resp_valid);

                        if (valid == 1) begin
                            send_to_sb_valid <= 1'b1;
                            ready_reg <= 1'b0;
                        end
                    end
                    
                end
            endcase
        end
    end

endmodule

